// LoadCodecs.h

#ifndef __LOADCODECS_H
#define __LOADCODECS_H

#include "Common/MyFixes.h"
//#include "Common/Buffer.h"
#include "Common/COM/ICoder.h"

#ifdef EXTERNAL_CODECS
#include "Common/System/DLL.h"
#endif

#include <vector>

namespace Common
{
namespace Archive
{

	typedef std::vector<unsigned char> ByteBuffer;

struct CDllCodecInfo
{
  CLSID Encoder;
  CLSID Decoder;
  bool EncoderIsAssigned;
  bool DecoderIsAssigned;
  int LibIndex;
  UINT32 CodecIndex;
};

#include "IArchive.h"

typedef IInArchive * (*CreateInArchiveP)();
typedef IOutArchive * (*CreateOutArchiveP)();

struct CArcExtInfo
{
  CString Ext;
  CString AddExt;
  CArcExtInfo() {}
  CArcExtInfo(const CString &ext): Ext(ext) {}
  CArcExtInfo(const CString &ext, const CString &addExt): Ext(ext), AddExt(addExt) {}
};


struct CArcInfoEx
{
  #ifdef EXTERNAL_CODECS
  int				LibIndex;
  UINT32			FormatIndex;
  CLSID				ClassID;
  #endif
  bool				UpdateEnabled;
  CreateInArchiveP	CreateInArchive;
  CreateOutArchiveP	CreateOutArchive;
  CString			Name;
  typedef std::vector<CArcExtInfo> ArrayExts;
  ArrayExts			Exts;
  #ifndef _SFX
  //CByteBuffer		StartSignature;
  ByteBuffer StartSignature;
  // CByteBuffer	FinishSignature;
  #ifdef NEW_FOLDER_INTERFACE
  CStringVector		AssociateExts;
  #endif
  #endif
  bool				KeepName;
  CString GetMainExt() const
  {
	if (Exts.empty())
	  return CString();
	return Exts[0].Ext;
  }
  int FindExtension(const CString &ext) const
  {
	for (size_t i = 0; i < Exts.size(); i++)
	  if (ext.CompareNoCase(Exts[i].Ext) == 0)
		return i;
	return -1;
  }
  CString GetAllExtensions() const
  {
	CString s;
	for (size_t i = 0; i < Exts.size(); i++)
	{
	  if (i > 0)
		s += ' ';
	  s += Exts[i].Ext;
	}
	return s;
  }

  void AddExts(const wchar_t* ext, const wchar_t* addExt);

  CArcInfoEx():
	#ifdef EXTERNAL_CODECS
	LibIndex(-1),
	#endif
	UpdateEnabled(false),
	CreateInArchive(0), CreateOutArchive(0),
	KeepName(false)
	#ifndef _SFX
	#endif
  {}
  CArcInfoEx(const CArcInfoEx &obj) : 
  #ifdef EXTERNAL_CODECS
	LibIndex(obj.LibIndex),
	FormatIndex(obj.FormatIndex),
	ClassID(obj.ClassID),
  #endif
	UpdateEnabled(obj.UpdateEnabled),
	CreateInArchive(obj.CreateInArchive),
	CreateOutArchive(obj.CreateOutArchive),
	Name(obj.Name),
	Exts(obj.Exts),
  #ifndef _SFX
	StartSignature(obj.StartSignature),
//	FinishSignature(obj.FinishSignature),
 #ifdef NEW_FOLDER_INTERFACE
	AssociateExts(obj.AssociateExts),
#endif
#endif
	KeepName(obj.KeepName)
  {

  }

  CArcInfoEx& operator= (const CArcInfoEx & obj)
  {
#ifdef EXTERNAL_CODECS
		  LibIndex =  obj.LibIndex;
		  FormatIndex = obj.FormatIndex;
		  ClassID =  obj.ClassID;
#endif
		  UpdateEnabled = obj.UpdateEnabled;
		  CreateInArchive = obj.CreateInArchive;
		  CreateOutArchive = obj.CreateOutArchive;
		  Name  = obj.Name;
		  Exts = obj.Exts;
#ifndef _SFX
		  StartSignature = obj.StartSignature;
		  //	FinishSignature = obj.FinishSignature;
#ifdef NEW_FOLDER_INTERFACE
		  AssociateExts = obj.AssociateExts;
#endif
#endif
		  KeepName = obj.KeepName;
	  return *this;
  }

};

#ifdef EXTERNAL_CODECS
typedef UINT32 (WINAPI *GetMethodPropertyFunc)(UINT32 index, PROPID propID, PROPVARIANT *value);
typedef UINT32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *interfaceID, void **outObject);


#ifdef NEW_FOLDER_INTERFACE
struct CCodecIcons
{
  struct CIconPair
  {
	CString		Ext;
	int			IconIndex;
	CIconPair() {}
	CIconPair(const CIconPair &obj) :
		Ext(obj.Ext),
		IconIndex(obj.IconIndex)
	{
	}
	CIconPair& operator=(const CIconPair & obj)
	{
		if (this == &obj)
			return *this;
		Ext = obj.Ext;
		IconIndex = obj.IconIndex;
		return *this;
	}
  };

  typedef std::vector<CIconPair> ArrayIconPair;
  ArrayIconPair IconPairs;
  void LoadIcons(HMODULE m);
  bool FindIconIndex(const CString &ext, int &iconIndex) const;
};
#endif

struct CCodecLib
#ifdef NEW_FOLDER_INTERFACE
: public CCodecIcons
#endif
{
  System::DLL::CLibrary		Lib;
  GetMethodPropertyFunc		GetMethodProperty;
  CreateObjectFunc			CreateObject;
  #ifdef NEW_FOLDER_INTERFACE
  CString					Path;
  void LoadIcons() { CCodecIcons::LoadIcons((HMODULE)Lib); }
  #endif
  
  CCodecLib(): GetMethodProperty(0) {}
  CCodecLib(const  CCodecLib &obj) :	   
#ifdef NEW_FOLDER_INTERFACE
		CCodecIcons(obj),
		Path(obj.Path),
#endif	
		Lib(obj.Lib),
	   GetMethodProperty(obj.GetMethodProperty),

	   CreateObject(obj.CreateObject)
   {
   }
  CCodecLib& operator=(const CCodecLib & obj)
  {
	  if (this == &obj)
		  return *this;
#ifdef NEW_FOLDER_INTERFACE
	  CCodecIcons::operator=(obj);
	  Path = obj.Path;
#endif	
	  Lib = obj.Lib;
	  GetMethodProperty = obj.GetMethodProperty;
	  CreateObject = obj.CreateObject;

	  return *this;
  }

};
#endif

class ATL_NO_VTABLE CCodecs:
	public CComObjectRootEx<CComSingleThreadModel>,
  #ifdef EXTERNAL_CODECS
  public ICompressCodecsInfo
  #else
  public IUnknown
  #endif
  
{
public:

	BEGIN_COM_MAP(CCodecs)
#ifndef EXTERNAL_CODECS
		COM_INTERFACE_ENTRY(ICompressCodecsInfo)
#endif		
	END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

  #ifdef EXTERNAL_CODECS
	typedef std::vector<CCodecLib> ArrayCodecLib;
	ArrayCodecLib Libs;
	typedef std::vector<CDllCodecInfo> ArrayDLLCodecInfo;
	ArrayDLLCodecInfo Codecs;

  #ifdef NEW_FOLDER_INTERFACE
  CCodecIcons InternalIcons;
  #endif

  HRESULT LoadCodecs();
  HRESULT LoadFormats();
  HRESULT LoadDll(const CString &path, bool needCheckDll);
  HRESULT LoadDllsFromFolder(const CString &folderPrefix);

  HRESULT CreateArchiveHandler(const CArcInfoEx &ai, void **archive, bool outHandler) const
  {
	return Libs[ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &__uuidof(IOutArchive) : & __uuidof(IInArchive), (void **)archive);
  }
  #endif

public:
	typedef std::vector<CArcInfoEx> ArrayArcInfoEx;
	ArrayArcInfoEx Formats;
  HRESULT Load();
  
  #ifndef _SFX
  size_t FindFormatForArchiveName(const CString &arcPath) const;
  size_t FindFormatForExtension(const CString &ext) const;
  size_t FindFormatForArchiveType(const CString &arcType) const;
  bool FindFormatForArchiveType(const CString &arcType, std::vector<int> &formatIndices) const;
  #endif

  #ifdef EXTERNAL_CODECS
  STDMETHOD(GetNumberOfMethods)(UINT32 *numMethods);
  STDMETHOD(GetProperty)(UINT32 index, PROPID propID, PROPVARIANT *value);
  STDMETHOD(CreateDecoder)(UINT32 index, const GUID *interfaceID, void **coder);
  STDMETHOD(CreateEncoder)(UINT32 index, const GUID *interfaceID, void **coder);
  #endif

  int GetCodecLibIndex(UINT32 index);
  bool GetCodecEncoderIsAssigned(UINT32 index);
  HRESULT GetCodecId(UINT32 index, UINT64 &id);
  CString GetCodecName(UINT32 index);

  HRESULT CreateInArchive(int formatIndex, IInArchive** ppArchive) const
  {
	const CArcInfoEx &ai = Formats[formatIndex];
	#ifdef EXTERNAL_CODECS
	if (ai.LibIndex < 0)
	#endif
	{
		*ppArchive = ai.CreateInArchive();
	  return S_OK;
	}
	#ifdef EXTERNAL_CODECS
	return CreateArchiveHandler(ai, (void **)ppArchive, false);
	#endif
  }
  
  HRESULT CreateOutArchive(int formatIndex, IOutArchive** archive) const
  {
	const CArcInfoEx &ai = Formats[formatIndex];
	#ifdef EXTERNAL_CODECS
	if (ai.LibIndex < 0)
	#endif
	{
	  *archive = ai.CreateOutArchive();
	  return S_OK;
	}
	#ifdef EXTERNAL_CODECS
	return CreateArchiveHandler(ai, (void **)&archive, true);
	#endif
  }
  size_t FindOutFormatFromName(const CString &name) const
  {
	for (size_t i = 0; i < Formats.size(); i++)
	{
	  const CArcInfoEx &arc = Formats[i];
	  if (!arc.UpdateEnabled)
		continue;
	  if (arc.Name.CompareNoCase(name) == 0)
		return i;
	}
	return -1;
  }

  #ifdef EXTERNAL_CODECS
	HRESULT CreateCoder(const CString &name, bool encode, ICompressCoder** coder) const;
  #endif

};

}
}
#endif
